#! /usr/bin/env python3

import argparse
import os
import requests

import gitlab

TABLE_HEADER = """
| Job | Regressions/max | Improvements/max | Total | Geomean | LNT report |
| --- | --------------- | ---------------- | ----- | ------- | ---------- |"""
TABLE_BODY = '\n| {job} | {regressions} / {max_regr} | {improvements} / {max_impr} | {total}' + \
             ' | {geomean} | {report} |'

def _create_link(text, url):
    """Create link in markdown format"""

    return '[{}]({})'.format(text, url)

def get_regression_discussion(discussions, username):
    """Get discussion with MR regression information

    :param discussions: all MR discussions
    :type discussions: gitlab.v4.objects.ProjectMergeRequestDiscussionManager or list
    :return: Discussion with MR regression information
    :rtype: gitlab.v4.objects.ProjectMergeRequestDiscussion
    """

    if isinstance(discussions, gitlab.v4.objects.ProjectMergeRequestDiscussionManager):
        discussions = discussions.list(all=True)
    for discussion in discussions:
        author = discussion.attributes['notes'][0]['author']
        if author['username'] == username:
            return discussion

def create_body(pipeline_url, job_name, job_url, regressions, improvements, total,
                max_regression, max_improvement, geomean, commit, lnt_url, body=None):
    """Create body of a note. If note already exists (parameter body is not None),
    add new information to existent body. Return generated body.
    """

    if not body:
        body = 'Pipeline: {}, commit: {}\n\n'.format(
            _create_link(pipeline_url.split('/')[-1], pipeline_url),
            commit)
        body += TABLE_HEADER
    job = _create_link(job_name, job_url)
    report = _create_link('report', lnt_url)
    body += TABLE_BODY.format(job=job,
                              regressions=regressions,
                              improvements=improvements,
                              total=total,
                              max_regr=max_regression,
                              max_impr=max_improvement,
                              geomean=geomean,
                              report=report)
    return body

def create_regression_discussion(mr, body):
    """Create discussion with MR regression information.

    :param mr: Merge request
    :type mr: gitlab.v4.objects.ProjectMergeRequest
    :param body: body of the discussion
    :type body: str
    :return: Created discussion
    :rtype: gitlab.v4.objects.ProjectMergeRequestDiscussion
    """

    return mr.discussions.create({'body': body})

def create_regression_note(discussion, body):
    """Create note with MR regression information to an existing discussion.

    :param discussion: Discussion to create a note
    :type mr: gitlab.v4.objects.ProjectMergeRequestDiscussion
    :param body: body of the discussion
    :type body: str
    :return: Created note
    :rtype: gitlab.v4.objects.ProjectMergeRequestDiscussionNote
    """

    return discussion.notes.create({'body': body})

def get_note_by_pipeline(discussion, pipeline_url, username):
    """Get note by the specified pipeline and for the specified user.

    :param discussion: note will be searched in this discussion
    :type discussion: class:'gitlab.v4.objects.ProjectMergeRequestDiscussion'
    :param pipeline_url: URL of the required pipeline
    :type pipeline_url: str
    :param username: username of an author of the required note
    :type username: str
    :return: note with <pipeline_url> in the body and created by <username>
    :rtype: class:'gitlab.v4.objects.ProjectMergeRequestDiscussionNote'
    """

    for note in discussion.attributes['notes']:
        if pipeline_url in note['body'] and username == note['author']['username']:
            return discussion.notes.get(note['id'])
    return None

if __name__ == '__main__':
    parser = argparse.ArgumentParser(
        description=f"""Update regression info in an MR thread. If the thread hasn't been created,
                        create new thread. If the thread already exists (script tries to find a
                        thread that created by the specified technical user), create a note in
                        it. If a regressions number is 0, resolve the thread.""",
        formatter_class=argparse.ArgumentDefaultsHelpFormatter)
    parser.add_argument('-u', '--url',
                        default=os.getenv('CI_SERVER_URL', 'https://rnd-gitlab-msc.huawei.com/'),
                        help='URL of the GitLab server')
    parser.add_argument('-a', '--access-token',
                        required=True,
                        help='GitLab private access token of the technical user')
    parser.add_argument('-t', '--tech-user',
                        required=True,
                        help='GitLab user that creates the discussion')
    parser.add_argument('-p', '--project-id',
                        required=True, type=int,
                        help='GitLab project ID')
    parser.add_argument('-m', '--mr-id',
                        required=True, type=int,
                        help='GitLab merge request ID')
    parser.add_argument('-j', '--job-name',
                        required=True,
                        help='GitLab CI job name')
    parser.add_argument('-r', '--regressions',
                        required=True, type=int,
                        help='Regressions number')
    parser.add_argument('-i', '--improvements',
                        required=True, type=int,
                        help='Imporvements number')
    parser.add_argument('--total',
                        required=True, type=int,
                        help='Total amount of tests')
    parser.add_argument('--max-regression',
                        required=True,
                        help='Maximum regression, %')
    parser.add_argument('--max-improvement',
                        required=True,
                        help='Maximum improvement, %')
    parser.add_argument('--geomean',
                        required=True,
                        help='Geometric mean, %')
    parser.add_argument('-c', '--commit',
                        required=True,
                        help='Hash of the checked commit')
    parser.add_argument('--pipeline-url',
                        required=True,
                        help='URL of the pipeline')
    parser.add_argument('--lnt-url',
                        help='LNT report URL')
    parser.add_argument('--job-url',
                        help='GitLab CI job URL')
    args = parser.parse_args()

    gl = gitlab.Gitlab(args.url, private_token=args.access_token)
    gl.auth()

    project = gl.projects.get(args.project_id)
    mr = project.mergerequests.get(args.mr_id)

    new_body = None
    note = None
    note_is_new = True
    discussion = get_regression_discussion(mr.discussions, args.tech_user)
    if discussion:
        note = get_note_by_pipeline(discussion, args.pipeline_url, args.tech_user)
        if note:
            new_body = note.body
            note_is_new = False

    new_body = create_body(args.pipeline_url, args.job_name, args.job_url,
                           args.regressions, args.improvements, args.total,
                           args.max_regression, args.max_improvement, args.geomean,
                           args.commit, args.lnt_url, new_body)

    if note:
        note.body = new_body
        note.save()
    elif discussion:
        create_regression_note(discussion, new_body)
    else:
        discussion = create_regression_discussion(mr, new_body)

    if note_is_new and args.regressions == 0:
        discussion.resolved = True
        discussion.save()
    elif args.regressions > 0:
        discussion.resolved = False
        discussion.save()
